fix: handle ids with multiple null bytes#22687
Open
SujalXplores wants to merge 1 commit into
Open
Conversation
wrapId, unwrapId, and the Lightning CSS filename encoder used String.prototype.replace with a string pattern, which only replaces the first match. Ids that contain more than one null byte (for example a CommonJS proxy wrapping a virtual module, \0commonjs-proxy:\0virtual) were left with raw null bytes after the first one. That breaks the documented contract that a wrapped id is a valid import URL with every \0 encoded as __x00__, and Lightning CSS treats a raw null byte as a string terminator. Use replaceAll so every null byte is encoded and decoded symmetrically.
Author
|
The failing job (Build&Test node-24, macos-latest) is a flaky beforeAll startup timeout in playground/import-assertion, unrelated to this change. The same commit passed on the other 5 matrix configs and all unit tests. A re-run should clear it. |
This was referenced Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #22690
Description
wrapIdandunwrapId(packages/vite/src/shared/utils.ts) and the Lightning CSS filename encoder (packages/vite/src/node/plugins/css.ts) encode/decode null bytes withString.prototype.replaceand a string pattern, which only replaces the first occurrence.When an id contains more than one null byte, only the first is handled. This happens when a virtual module is wrapped by another plugin, for example a CommonJS proxy around a virtual module produces an id like
\0commonjs-proxy:\0virtual.Consequences:
wrapIdis documented to make an id URL-safe by encoding every\0as__x00__(seeNULL_BYTE_PLACEHOLDERinshared/constants.ts), but the wrapped id still contained a raw null byte, which is not valid in an import URL.unwrapIdonly restored the first placeholder, so decoding was not symmetric.css.tsthe existing comment notes Lightning CSS treats a null byte as a string terminator, so a leftover null byte truncates the filename.Fix
Use
replaceAllin all three places so every null byte is encoded and decoded.replaceAllis already used widely in the codebase and is supported by theenginesfield (^20.19.0 || >=22.12.0).Tests
Added
packages/vite/src/shared/__tests__/utils.spec.tscoveringwrapId/unwrapId, including ids with multiple null bytes. The multi-null-byte cases fail onmainand pass with this change.